import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { createLogger } from '@/utils/logger';
import { toPinyin } from './utils';

interface ApifoxConfig {
  docsUrl: string;
  includeTags: string[];
  excludeTags?: string[];
  requestInstance: string;
}

interface OpenAPISpec {
  paths: Record<string, Record<string, any>>;
  components?: {
    schemas?: Record<string, any>;
  };
}

interface APIEndpoint {
  path: string;
  method: string;
  operationId?: string;
  tags?: string[];
  summary?: string;
  parameters?: any[];
  requestBody?: any;
  responses?: Record<string, any>;
}

export class ApifoxGenerator {
  private config: ApifoxConfig;
  private targetPath: string;
  private debug: boolean;
  private logger = createLogger();

  constructor(config: ApifoxConfig, targetPath: string, debug = false) {
    this.config = config;
    this.targetPath = targetPath;
    this.debug = debug;
    
    if (debug) {
      this.logger.setLogLevel('debug');
    }
  }

  async generate(): Promise<void> {
    try {
      // 1. 获取OpenAPI文档
      this.logger.info('Fetching OpenAPI documentation...');
      const openApiSpec = await this.fetchOpenAPISpec();
      
      // 2. 解析API端点
      this.logger.info('Parsing API endpoints...');
      const endpoints = this.parseEndpoints(openApiSpec);
      
      // 3. 过滤端点
      this.logger.info('Filtering endpoints by tags...');
      const filteredEndpoints = this.filterEndpoints(endpoints);
      
      // 4. 按标签分组
      this.logger.info('Grouping endpoints by tags...');
      const groupedEndpoints = this.groupEndpointsByTags(filteredEndpoints);
      
      // 5. 生成文件
      this.logger.info('Generating TypeScript files...');
      await this.generateFiles(groupedEndpoints, openApiSpec);
      
    } catch (error) {
      this.logger.error('Generation failed:', error);
      throw error;
    }
  }

  private async fetchOpenAPISpec(): Promise<OpenAPISpec> {
    try {
      const response = await axios.get(this.config.docsUrl);
      return response.data;
    } catch (error) {
      throw new Error(`Failed to fetch OpenAPI spec from ${this.config.docsUrl}: ${error}`);
    }
  }

  private parseEndpoints(spec: OpenAPISpec): APIEndpoint[] {
    const endpoints: APIEndpoint[] = [];
    
    for (const [path, methods] of Object.entries(spec.paths)) {
      for (const [method, details] of Object.entries(methods)) {
        if (['get', 'post', 'put', 'delete', 'patch'].includes(method.toLowerCase())) {
          endpoints.push({
            path,
            method: method.toLowerCase(),
            ...details
          });
        }
      }
    }
    
    return endpoints;
  }

  private filterEndpoints(endpoints: APIEndpoint[]): APIEndpoint[] {
    return endpoints.filter(endpoint => {
      const tags = endpoint.tags || [];
      
      // 检查是否包含在includeTags中
      const hasIncludeTag = this.config.includeTags.some(tag => 
        tags.includes(tag)
      );
      
      // 检查是否在excludeTags中
      const hasExcludeTag = this.config.excludeTags?.some(tag => 
        tags.includes(tag)
      ) || false;
      
      return hasIncludeTag && !hasExcludeTag;
    });
  }

  private groupEndpointsByTags(endpoints: APIEndpoint[]): Record<string, APIEndpoint[]> {
    const groups: Record<string, APIEndpoint[]> = {};
    
    for (const endpoint of endpoints) {
      const tags = endpoint.tags || [];
      
      for (const tag of tags) {
        if (this.config.includeTags.includes(tag)) {
          if (!groups[tag]) {
            groups[tag] = [];
          }
          groups[tag].push(endpoint);
        }
      }
    }
    
    return groups;
  }

  private async generateFiles(
    groupedEndpoints: Record<string, APIEndpoint[]>, 
    spec: OpenAPISpec
  ): Promise<void> {
    const controllerPath = path.join(this.targetPath, 'controller');
    await fs.ensureDir(controllerPath);

    for (const [tag, endpoints] of Object.entries(groupedEndpoints)) {
      // 生成文件名（拼音 + 大驼峰）
      const fileName = this.generateFileName(tag);
      const filePath = path.join(controllerPath, `${fileName}.ts`);
      
      this.logger.info(`Generating ${fileName}.ts...`);
      
      // 生成文件内容
      const fileContent = this.generateFileContent(tag, endpoints, spec);
      
      // 写入文件
      await fs.writeFile(filePath, fileContent, 'utf-8');
    }
  }

  private generateFileName(tag: string): string {
    // 将中文转换为拼音，然后转换为大驼峰格式
    const pinyin = toPinyin(tag);
    return this.toPascalCase(pinyin);
  }

  private generateFileContent(tag: string, endpoints: APIEndpoint[], spec: OpenAPISpec): string {
    const imports = [`import request from '${this.config.requestInstance}';`];
    const types: string[] = [];
    const functions: string[] = [];

    for (const endpoint of endpoints) {
      const functionName = this.generateFunctionName(endpoint.path);
      const { inputType, resultType, typeDefinitions } = this.generateTypes(endpoint, spec);
      
      types.push(...typeDefinitions);
      
      // 生成函数
      const func = this.generateFunction(endpoint, functionName, inputType, resultType);
      functions.push(func);
    }

    // 组合文件内容
    const content = [
      ...imports,
      '',
      ...types,
      '',
      ...functions
    ].join('\n');

    return content;
  }

  private generateFunctionName(path: string): string {
    // 将路径转换为函数名，如 /shop/v1/cate/list -> ShopV1CateList
    return path
      .split('/')
      .filter(segment => segment && segment !== 'v1' && segment !== 'v2') // 可选：过滤版本号
      .map(segment => this.toPascalCase(segment))
      .join('');
  }

  private generateTypes(endpoint: APIEndpoint, spec: OpenAPISpec): {
    inputType: string;
    resultType: string;
    typeDefinitions: string[];
  } {
    const functionName = this.generateFunctionName(endpoint.path);
    const inputType = `${functionName}Input`;
    const resultType = `${functionName}Result`;
    const typeDefinitions: string[] = [];

    // 生成输入类型
    const inputTypeDefinition = this.generateInputType(endpoint, inputType);
    if (inputTypeDefinition) {
      typeDefinitions.push(inputTypeDefinition);
    }

    // 生成输出类型
    const resultTypeDefinition = this.generateResultType(endpoint, resultType, spec);
    if (resultTypeDefinition) {
      typeDefinitions.push(resultTypeDefinition);
    }

    return {
      inputType: inputTypeDefinition ? inputType : 'any',
      resultType: resultTypeDefinition ? resultType : 'any',
      typeDefinitions
    };
  }

  private generateInputType(endpoint: APIEndpoint, typeName: string): string | null {
    const properties: string[] = [];

    // 处理路径参数
    if (endpoint.parameters) {
      for (const param of endpoint.parameters) {
        if (param.in === 'path' || param.in === 'query') {
          const required = param.required ? '' : '?';
          const type = this.mapOpenAPIType(param.schema?.type || 'string');
          properties.push(`  ${param.name}${required}: ${type};`);
        }
      }
    }

    // 处理请求体
    if (endpoint.requestBody?.content?.['application/json']?.schema) {
      const schema = endpoint.requestBody.content['application/json'].schema;
      const bodyProperties = this.generatePropertiesFromSchema(schema);
      properties.push(...bodyProperties);
    }

    if (properties.length === 0) {
      return null;
    }

    return `export interface ${typeName} {\n${properties.join('\n')}\n}`;
  }

  private generateResultType(endpoint: APIEndpoint, typeName: string, spec: OpenAPISpec): string | null {
    const response200 = endpoint.responses?.['200'];
    if (!response200?.content?.['application/json']?.schema) {
      return null;
    }

    const schema = response200.content['application/json'].schema;
    const properties = this.generatePropertiesFromSchema(schema);

    if (properties.length === 0) {
      return null;
    }

    return `export interface ${typeName} {\n${properties.join('\n')}\n}`;
  }

  private generatePropertiesFromSchema(schema: any): string[] {
    const properties: string[] = [];

    if (schema.properties) {
      for (const [propName, propSchema] of Object.entries(schema.properties as Record<string, any>)) {
        const required = schema.required?.includes(propName) ? '' : '?';
        const type = this.mapOpenAPIType(propSchema.type);
        const comment = propSchema.title || propSchema.description;
        
        if (comment) {
          properties.push(`  /** ${comment} */`);
        }
        properties.push(`  ${propName}${required}: ${type};`);
      }
    }

    return properties;
  }

  private generateFunction(endpoint: APIEndpoint, functionName: string, inputType: string, resultType: string): string {
    const hasInput = inputType !== 'any';
    const params = hasInput ? `params: ${inputType}` : '';
    const method = endpoint.method.toUpperCase();
    const path = endpoint.path;

    let requestCall = '';
    if (method === 'GET') {
      requestCall = hasInput ? 
        `request.get<${resultType}>('${path}', { params })` :
        `request.get<${resultType}>('${path}')`;
    } else {
      requestCall = hasInput ?
        `request.${endpoint.method}<${resultType}>('${path}', params)` :
        `request.${endpoint.method}<${resultType}>('${path}')`;
    }

    const comment = endpoint.summary ? `/** ${endpoint.summary} */\n` : '';

    return `${comment}export const ${this.toCamelCase(functionName)} = (${params}) => {
  return ${requestCall};
};`;
  }

  private mapOpenAPIType(type: string): string {
    switch (type) {
      case 'integer':
        return 'number';
      case 'string':
        return 'string';
      case 'boolean':
        return 'boolean';
      case 'array':
        return 'any[]';
      case 'object':
        return 'any';
      default:
        return 'any';
    }
  }

  private toPascalCase(str: string): string {
    return str.replace(/(?:^|[-_])(\w)/g, (_, char) => char.toUpperCase());
  }

  private toCamelCase(str: string): string {
    const pascalCase = this.toPascalCase(str);
    return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
  }
}
